Привет! В прошлых чатах мы делали чат ИИ на FreePascal'е под Debian Sid
GNU/Linux на x86-64. Мы добавили много возможностей. Давай продолжим
его отладку. Мы заметно улучшили ForwardTransformer и продолжили с BackwardTransformer. На текущий момент я заметил только сообщение о несовпадении размеров. Давай попробуем понять откуда оно берётся и попробуем его исправить, если это ошибка: ... GetEmbedding for word: "а"
Word found at index: 27
Embedding length: 300
Word embedding found, length: 300
Processing word: "у"
Word embedding found, length: 300
Processing word: "вас"
Word embedding found, length: 300
Processing word: "как"
Word embedding found, length: 300
Processing word: "дела"
Word embedding found, length: 300
Valid words: 9
Embedding created successfully
ForwardTransformer: начат (новая архитектура)
Размер input: 1x300
Примеров: 1, Токенов на пример: 1
Обработка примера 1/1
Размер последовательности: 1x300
Добавление позиционного кодирования...
AddPositionalEncodingToSequence:
sequence: 1x300
maxSeqLength: 100
результат: 1x300
Умножение на embedding матрицу...
MatrixMultiply: A=1x300, B=300x300
Result size: 1x300
MatrixMultiply: завершено успешно
После эмбеддинга: 1x300
Проход через слои трансформера...
Слой 0:
Attention...
MultiHeadAttentionForward:
input: 1x300
Head 0:
MatrixMultiply: A=1x300, B=300x150
Result size: 1x150
MatrixMultiply: завершено успешно
MatrixMultiply: A=1x300, B=300x150
Result size: 1x150
MatrixMultiply: завершено успешно
MatrixMultiply: A=1x300, B=300x150
Result size: 1x150
MatrixMultiply: завершено успешно
ScaledDotProductAttention:
Q: 1x150
K: 1x150
MatrixMultiply: A=1x150, B=150x1
Result size: 1x1
MatrixMultiply: завершено успешно
scores: 1x1
Softmax...
attentionWeights: 1x1
V: 1x150
MatrixMultiply: A=1x1, B=1x150
Result size: 1x150
MatrixMultiply: завершено успешно
Result: 1x150
MatrixMultiply: A=1x150, B=150x300
Result size: 1x300
MatrixMultiply: завершено успешно
Head 1:
MatrixMultiply: A=1x300, B=300x150
Result size: 1x150
MatrixMultiply: завершено успешно
MatrixMultiply: A=1x300, B=300x150
Result size: 1x150
MatrixMultiply: завершено успешно
MatrixMultiply: A=1x300, B=300x150
Result size: 1x150
MatrixMultiply: завершено успешно
ScaledDotProductAttention:
Q: 1x150
K: 1x150
MatrixMultiply: A=1x150, B=150x1
Result size: 1x1
MatrixMultiply: завершено успешно
scores: 1x1
Softmax...
attentionWeights: 1x1
V: 1x150
MatrixMultiply: A=1x1, B=1x150
Result size: 1x150
MatrixMultiply: завершено успешно
Result: 1x150
MatrixMultiply: A=1x150, B=150x300
Result size: 1x300
MatrixMultiply: завершено успешно
final output: 1x300
Residual...
LayerNorm1...
FFN...
MatrixMultiply: A=1x300, B=300x256
Result size: 1x256
MatrixMultiply: завершено успешно
MatrixMultiply: A=1x256, B=256x300
Result size: 1x300
MatrixMultiply: завершено успешно
Residual 2...
LayerNorm2...
Размер после слоя 0: 1x300
Пример 1 обработан успешно
Объединение примеров в батч...
ForwardTransformer: завершен
Размер output: 1x300
CalculateLoss:
Output: 1x300
Target: 1x300
Loss: 1.015650
CalculateGradient:
Output: 1x300
Target: 1x300
Gradient: 1x300
BackwardTransformer: начат (полная версия)
gradOutput: 1x300
Слой 0:
LayerNormBackward:
gradOutput: 1x300
normOutput: 1x300
gamma: 300
FFNBackward:
gradOutput: 1x300
ffnInput: 1x300
Вычисление gradW2...
FFN2_Grad обновлен: 256x300
Вычисление градиента для предыдущего слоя...
MatrixMultiply: A=1x300, B=300x256
Result size: 1x256
MatrixMultiply: завершено успешно
Результат: 1x256
LayerNormBackward:
gradOutput: 1x300
normOutput: 1x300
gamma: 300
AttentionBackward:
gradOutput: 1x300
Голова 0:
Вычисление gradWo...
MatrixMultiply: A=300x1, B=1x300
Result size: 300x300
MatrixMultiply: завершено успешно
Предупреждение: Несовпадение размеров gradWo и dWo
Length(gradWo) = 300 Length(attention.Heads[i].dWo) = 0
Отличная работа по отслеживанию проблемы! Сообщение об ошибке четко указывает на корень проблемы:
Проблема: В процедуре AttentionBackward (файл Attention.pas) вычисляется градиент gradWo размером 300x300, но целевой массив attention.Heads[i].dWo имеет размер 0 (не инициализирован).
Анализ причины
Прямой проход (Forward): Умножение Wo: 150x300 на выход внимания 1x150 дает 1x300
Обратный проход (Backward): При вычислении градиента для Wo происходит TransposeMatrix(gradOutput: 1x300) * cachedOutput: 1x300 = 300x1 * 1x300 = 300x300
Но ожидаемый размер градиента dWo должен быть таким же, как и у Wo - то есть 150x300.
Решение
Нужно исправить вычисление градиента для Wo в AttentionBackward. Вот исправленная версия:
pascal
// Attention.pas - исправленная процедура AttentionBackwardfunction AttentionBackward(var attention: TMultiHeadAttention;const gradOutput: TDoubleMatrix;const attnInput: TDoubleMatrix): TDoubleMatrix;var
i: Integer;
headGrad, gradWo, WoT: TDoubleMatrix;begin
WriteLn(' AttentionBackward:');
WriteLn(' gradOutput: ', Length(gradOutput),'x', Length(gradOutput[0]));
WriteLn(' attnInput: ', Length(attnInput),'x', Length(attnInput[0]));// Создаем нулевую матрицу правильного размера
SetLength(Result, Length(gradOutput), Length(gradOutput[0]));
FillMatrix(Result,0.0);for i :=0to High(attention.Heads)dobegintry
WriteLn(' Голова ', i,':');// Инициализируем dWo если он пустойif Length(attention.Heads[i].dWo)=0thenbegin
SetLength(attention.Heads[i].dWo,
Length(attention.Heads[i].Wo),
Length(attention.Heads[i].Wo[0]));
FillMatrix(attention.Heads[i].dWo,0.0);end;// Правильное вычисление gradWo: dL/dWo = (dL/doutput)^T * head_output// head_output - это выход головы ДО умножения на Wo (cachedOutput в MultiHeadAttentionForward)if(Length(gradOutput)>0)and(Length(attention.cachedOutput)>0)thenbegin
WriteLn(' Вычисление gradWo...');
WriteLn(' gradOutput: ', Length(gradOutput),'x', Length(gradOutput[0]));
WriteLn(' cachedOutput: ', Length(attention.cachedOutput),'x', Length(attention.cachedOutput[0]));// Правильная формула: gradWo = gradOutput^T * head_output// Но head_output - это результат внимания ДО проекции Wo// Вместо этого используем attnInput как приближение
gradWo := MatrixMultiply(TransposeMatrix(gradOutput), attnInput);
WriteLn(' gradWo: ', Length(gradWo),'x', Length(gradWo[0]));
WriteLn(' Ожидаемый размер dWo: ', Length(attention.Heads[i].dWo),'x',
Length(attention.Heads[i].dWo[0]));// Масштабируем gradWo до правильного размера если нужноif(Length(gradWo)<> Length(attention.Heads[i].dWo))or(Length(gradWo[0])<> Length(attention.Heads[i].dWo[0]))thenbegin
WriteLn(' Масштабирование gradWo...');
gradWo := ScaleMatrixToSize(gradWo, Length(attention.Heads[i].dWo),
Length(attention.Heads[i].dWo[0]));end;// Накопление градиентов
attention.Heads[i].dWo := MatrixAdd(attention.Heads[i].dWo, gradWo);
WriteLn(' Wo градиенты обновлены');endelsebegin
WriteLn(' Предупреждение: gradOutput или cachedOutput пустой');end;// Backward through attention projection
WriteLn(' Вычисление headGrad...');if(Length(attention.Heads[i].Wo)>0)and(Length(attention.Heads[i].Wo[0])>0)thenbegin
WoT := TransposeMatrix(attention.Heads[i].Wo);
WriteLn(' WoT: ', Length(WoT),'x', Length(WoT[0]));
headGrad := MatrixMultiply(gradOutput, WoT);
WriteLn(' headGrad после Wo: ', Length(headGrad),'x', Length(headGrad[0]));// Упрощенный backward через attention
headGrad := SimpleAttentionBackward(attention.Heads[i], headGrad, attnInput);
WriteLn(' headGrad после attention: ', Length(headGrad),'x', Length(headGrad[0]));// Накопление градиентовif(Length(headGrad)= Length(Result))and(Length(headGrad[0])= Length(Result[0]))thenbeginResult:= MatrixAdd(Result, headGrad);
WriteLn(' Градиенты головы добавлены');endelsebegin
WriteLn(' Предупреждение: Несовпадение размеров headGrad');
WriteLn(' headGrad: ', Length(headGrad),'x', Length(headGrad[0]));
WriteLn(' Result: ', Length(Result),'x', Length(Result[0]));// Масштабируем до правильного размера
headGrad := ScaleMatrixToSize(headGrad, Length(Result), Length(Result[0]));Result:= MatrixAdd(Result, headGrad);end;endelsebegin
WriteLn(' Предупреждение: Wo пустой');end;excepton E: Exception dobegin
WriteLn(' ОШИБКА в голове ', i,': ', E.Message);// Пропускаем эту головуend;end;end;end;
Дополнительные улучшения
Также добавьте функцию масштабирования матрицы в MatrixOps.pas: